﻿#include "pch.h"
#include "sbhs.h"

// 외부
extern void _k_type_op_init(void);
extern void _k_type_value_init();
extern void _k_type_base_init(void);
extern void _k_type_red_init(void);
extern void _k_type_red_disp(void);

#define USE_TYPE_LOCK	1

#if USE_TYPE_LOCK
#define TYPE_LOCK		K_LOCK(k_impl_type.lock)
#define TYPE_UNLOCK		K_UNLOCK(k_impl_type.lock)
#else
#define TYPE_LOCK		kh_lock()
#deifne TYPE_UNLOCK		kh_unlock()
#endif


//////////////////////////////////////////////////////////////////////////
// 타입 정보

// 타입 해시
typedef struct kTiNode
{
	struct kTiNode* sib;
	struct kTiNode* next;
	struct kTiNode* prev;

	ksize_t hash;
	kuint key;
	kType value;
} kTiNode;

typedef struct kTiHash
{
	ksize_t bucket;
	ksize_t count;
	kTiNode** nodes;
	kTiNode* frst;
	kTiNode* last;
} kTiHash;

// 구현
static struct kImplType
{
	kint rednext;
	kint cmnnext;

	kcham chksize;

	kTiHash hash;
	khParamShed* prms;

	kTypeInfo* rtis[K_MAX_TYPE_LIMIT];

	kLock lock;
} k_impl_type =
{
	0,
	K_MAX_TYPE_LIMIT,

	FALSE,

	{0, },
	NULL,

	{0, },

	KLOCK_INIT
};

//
kTypeInfo** k_type_infos = k_impl_type.rtis;

//
static void _k_type_delete(kType type);

// 초기화
void _k_type_init(void)
{
	k_impl_type.hash.count = 0;
	k_impl_type.hash.bucket = K_MIN_HASH;
	k_impl_type.hash.nodes = k_new_zero(K_MIN_HASH, kTiNode*);

	// 파람
	k_impl_type.prms = kh_prmshed_new();

	// 따라오는 애들 초기화
	_k_type_op_init();
	_k_type_value_init();
	_k_type_base_init();
	_k_type_red_init();
}

// 제거
void _k_type_disp(void)
{
	kTiNode* node;
	kTiNode* next;

	// 따라오는 애들 먼저 제거
	_k_type_red_disp();

	// 타입 제거
	for (node = k_impl_type.hash.frst; node; node = node->next)
		_k_type_delete(node->value);

	for (node = k_impl_type.hash.frst; node; node = next)
	{
		next = node->next;
		k_delete(node);
	}

	k_delete(k_impl_type.hash.nodes);

	kh_prmshed_delete(k_impl_type.prms);
}

//
static void _k_tihash_test(void)
{
	ksize_t cnt = k_impl_type.hash.count;
	ksize_t bkt = k_impl_type.hash.bucket;

	if ((bkt >= 3 * cnt && bkt > K_MIN_HASH) || (3 * bkt <= cnt && bkt < K_MAX_HASH))
	{
		ksize_t i, newbucket;
		kTiNode** newnodes;

		newbucket = k_primeclose(cnt);
		newbucket = K_CLAMP(newbucket, K_MIN_HASH, K_MAX_HASH);
		newnodes = k_new_zero(newbucket, kTiNode*);

		for (i = 0; i < bkt; i++)
		{
			kTiNode* node;
			kTiNode* next;
			ksize_t hashmask;

			for (node = k_impl_type.hash.nodes[i]; node; node = next)
			{
				next = node->sib;
				hashmask = node->hash % newbucket;
				node->sib = newnodes[hashmask];
				newnodes[hashmask] = node;
			}
		}

		k_delete(k_impl_type.hash.nodes);
		k_impl_type.hash.nodes = newnodes;
		k_impl_type.hash.bucket = newbucket;
	}
}

//
static kTiNode** _k_tihash_lookup(kuint key, ksize_t* hash)
{
	ksize_t h = k_ptrhash(K_CAST_UINT_TO_PTR(key));
	kTiNode** ln = &k_impl_type.hash.nodes[key % k_impl_type.hash.bucket];
	kTiNode* lnn;

	while ((lnn = *ln) != NULL)
	{
		if (lnn->hash == h && lnn->key == key)
			break;

		ln = &lnn->sib;
	}

	if (hash)
		*hash = h;

	return ln;
}

//
static void _k_tihash_add(kuint key, kType value)
{
	ksize_t hash;
	kTiNode** n;
	kTiNode* nn;

	n = _k_tihash_lookup(key, &hash);
	nn = *n;

	if (nn)
	{
		// 대체 -> 안함
	}
	else
	{
		nn = k_new_1(kTiNode);
		nn->sib = NULL;
		nn->hash = hash;
		nn->key = key;
		nn->value = value;

		//
		if (k_impl_type.hash.frst)
			k_impl_type.hash.frst->prev = nn;
		else
			k_impl_type.hash.last = nn;

		nn->next = k_impl_type.hash.frst;
		nn->prev = NULL;

		k_impl_type.hash.frst = nn;

		//
		*n = nn;
		k_impl_type.hash.count++;

		//
		_k_tihash_test();
	}
}

//
static kType _k_tihash_get(kuint key)
{
	kTiNode** n;
	kTiNode* nn;

	n = _k_tihash_lookup(key, NULL);
	nn = *n;

	return nn ? nn->value : 0;
}

// 타입 지우기
static void _k_type_delete(kType type)
{
	kTypeInfo* info = k_tmnt_look_type(type);

	if (info->type_fini)
		info->type_fini(type);

	k_delete(info->desc);
	k_delete(info->children);
	k_delete(info);
}

// 크기 검사
static void _k_tmnt_check_size(const char* name, kuint size)
{
	if (k_impl_type.chksize)
	{
		kuint v = size % 16;

		if (v)
			k_tracef(KLV_INFO, "k.type", "size of '%s' is not aligned. (size:%d, quota:%d)", name, size, v);
	}
}

// 이름 검사
static kcham _k_tmnt_check_name(const char* name)
{
	if (!name[0] && !name[1])
		k_tracef(KLV_WARNING, "k.type", "type name '%s' has too short name to use.", name);

	if (k_tmnt_get_type(name))
	{
		k_tracef(KLV_ERROR, "k.type", "type '%s' is already exist.", name);
		return FALSE;
	}

	return TRUE;
}

// 상속 검사
static kTypeInfo* _k_tmnt_check_derived(kType parent, const char* name)
{
	kTypeInfo* info;

	info = k_tmnt_look_type(parent);

	if (!info)
	{
		k_tracef(KLV_ERROR, "k.type", "cannot define type '%s'. because parent type is missing.", name);
		return NULL;
	}

	if (info->flags & KTIF_SEALED)
	{
		k_tracef(KLV_ERROR, "k.type", "cannot defined type '%s'. becaus parent type '%s' is sealed.", name, info->name);
		return NULL;
	}

	return info;
}

// 빨간 타입
static kTypeInfo* _k_tmnt_create_red(kuint id, kuint sym, const kTypeDecl* decl, kTypeInfo* pinfo)
{
	kTypeInfo* info;
	kType type;
	kuint scnt, size, i;

	scnt = pinfo ? pinfo->scnt + 1 : 0;
	size = sizeof(kTypeInfo)+(sizeof(kType)* (1 + scnt + 1)); // info + self + super + NULL
	info = (kTypeInfo*)k_new_zero(size, kbyte);

	type = id;
	info->id = id;

	info->name = k_symstr(sym);
	info->sym = sym;

	info->size = decl->size;
	info->flags = decl->flags | KTIF_RED;

	info->type_init = decl->type_init;
	info->type_fini = decl->type_fini;

	info->func_cnst = decl->func_cnst;
	info->func_disp = decl->func_disp;

	if (pinfo)
	{
		info->roam = pinfo->roam;
		info->flags |= pinfo->flags & (KTIF_VALUE | KTIF_REF);

		info->scnt = scnt;
		info->isval = pinfo->isval;
		info->isref = pinfo->isref;

		memcpy(&info->optbl, &pinfo->optbl, sizeof(struct khOpTable));

		info->func_key = pinfo->func_key;
		info->func_hash = pinfo->func_hash;
		info->func_eq = pinfo->func_eq;

		if (!info->type_init)   info->type_init = pinfo->type_init;
		if (!info->type_fini)   info->type_fini = pinfo->type_fini;
		if (!info->func_cnst)   info->func_cnst = pinfo->func_cnst;
		if (!info->func_disp)   info->func_disp = pinfo->func_disp;

		info->parent = pinfo;
		info->supers[0] = type;
		memcpy(&info->supers[1], pinfo->supers, sizeof(kType)* (1 + pinfo->scnt + 1));

		i = pinfo->ccnt++;
		pinfo->children = k_renew(pinfo->children, pinfo->ccnt, kType);
		pinfo->children[i] = type;
	}
	else
	{
		info->scnt = scnt;
		info->isval = (decl->flags & KTIF_VALUE) != 0;
		info->isref = (decl->flags & KTIF_REF) != 0;

		info->supers[0] = type;
		info->supers[1] = 0;
	}

	// 등록
	TYPE_LOCK;
	k_impl_type.rtis[type] = info;
	_k_tihash_add(sym, type);
	TYPE_UNLOCK;

	// 초기화
	if (decl->type_init)
		decl->type_init(type);

	return info;
}

// 그냥
static kTypeInfo* _k_tmnt_create_cmn(kuint id, kuint sym, const kTypeDecl* decl, kTypeInfo* pinfo)
{
	kTypeInfo* info;
	kType type;
	kuint scnt, size, i;

	scnt = pinfo ? pinfo->scnt + 1 : 0;
	size = sizeof(kTypeInfo)+(sizeof(kType)* (1 + scnt + 1)); // info+self+super+NULL
	info = (kTypeInfo*)k_new_zero(size, kbyte);

	type = (kType)info;
	info->id = id;

	info->name = k_symstr(sym);
	info->sym = sym;

	info->size = decl->size;
	info->flags = decl->flags;

	info->type_init = decl->type_init;
	info->type_fini = decl->type_fini;

	info->func_cnst = decl->func_cnst;
	info->func_disp = decl->func_disp;

	if (pinfo)
	{
		info->roam = pinfo->roam;
		info->flags |= pinfo->flags & (KTIF_VALUE | KTIF_REF);

		info->scnt = scnt;
		info->isval = pinfo->isval;
		info->isref = pinfo->isref;

		memcpy(&info->optbl, &pinfo->optbl, sizeof(struct khOpTable));

		info->func_key = pinfo->func_key;
		info->func_hash = pinfo->func_hash;
		info->func_eq = pinfo->func_eq;

		if (!info->type_init)   info->type_init = pinfo->type_init;
		if (!info->type_fini)   info->type_fini = pinfo->type_fini;
		if (!info->func_cnst)   info->func_cnst = pinfo->func_cnst;
		if (!info->func_disp)   info->func_disp = pinfo->func_disp;

		info->parent = pinfo;
		info->supers[0] = type;
		memcpy(&info->supers[1], pinfo->supers, sizeof(kType)* (1 + pinfo->scnt + 1));

		i = pinfo->ccnt++;
		pinfo->children = k_renew(pinfo->children, pinfo->ccnt, kType);
		pinfo->children[i] = type;
	}
	else
	{
		info->scnt = scnt;
		info->isval = (decl->flags & KTIF_VALUE) != 0;
		info->isref = (decl->flags & KTIF_REF) != 0;

		info->supers[0] = type;
		info->supers[1] = 0;
	}

	// 등록
	TYPE_LOCK;
	_k_tihash_add(sym, type);
	TYPE_UNLOCK;

	// 초기화
	if (decl->type_init)
		decl->type_init(type);

	return info;
}

//////////////////////////////////////////////////////////////////////////

// 타입 얻기
kType k_tmnt_get_type(const char* name)
{
	kuint sym = k_symtry(name);
	return k_tmnt_find_type(sym);
}

// 심볼로 타입 얻기
kType k_tmnt_find_type(kuint sym)
{
	kType ret;

	k_return_value_if_fail(sym != 0, 0);

	TYPE_LOCK;
	ret = _k_tihash_get(sym);
	TYPE_UNLOCK;

	return ret;
}

// 정보 얻기
kTypeInfo* k_tmnt_look_type(kType type)
{
	kh_init();

	if (type < K_MAX_TYPE_LIMIT)
		return k_impl_type.rtis[type];
	else
		return (kTypeInfo*)type;
}

// 등록
kType k_tmnt_reg(const kchar* name, const kTypeDecl* decl, kType parent)
{
	kTypeInfo* info;
	kTypeInfo* pinfo;

	k_return_value_if_fail(name != NULL, 0);
	k_return_value_if_fail(decl != NULL, 0);

	if (!_k_tmnt_check_name(name))
		return 0;

	if (parent == KTYPE_NULL)
		pinfo = NULL;
	else
	{
		pinfo = _k_tmnt_check_derived(parent, name);

		if (!pinfo)
		{
			k_tracef(KLV_ERROR, "k.type", "the parent of red type '%s' is not exist.", name);
			return 0;
		}
	}

	_k_tmnt_check_size(name, decl->size);

	info = _k_tmnt_create_cmn(k_impl_type.cmnnext++, k_symstt(name), decl, pinfo);

	return info->supers[0];
}

// 등록
kType k_tmnt_reg_red(kint id, const kchar* name, const kTypeDecl* decl, kType parent)
{
	kTypeInfo* info;
	kTypeInfo* pinfo;

	k_return_value_if_fail(name != NULL, 0);
	k_return_value_if_fail(decl != NULL, 0);

	if (id >= K_MAX_TYPE_LIMIT)
	{
		k_tracef(KLV_ERROR, "k.type", "attempt to register of red type '%s' with invalid id %u.", name, id);
		return 0;
	}

	if (!_k_tmnt_check_name(name))
		return 0;

	info = k_tmnt_look_type(id);

	if (info)
	{
		k_tracef(KLV_ERROR, "k.type", "red type '%s' is already exist as '%s'.", name, info->name);
		return 0;
	}

	if (parent == 0)
		pinfo = NULL;
	else
	{
		pinfo = _k_tmnt_check_derived(parent, name);

		if (!pinfo)
		{
			k_tracef(KLV_ERROR, "k.type", "the parent of red type '%s' is not exist.", name);
			return 0;
		}

		if (pinfo->id >= id)
		{
			k_tracef(KLV_ERROR, "k.type", "the parent of red type '%s' must be red type.", name);
			return 0;
		}
	}

	_k_tmnt_check_size(name, decl->size);

	TYPE_LOCK;
	if (k_impl_type.rednext == id)
		k_impl_type.rednext++;
	TYPE_UNLOCK;

	info = _k_tmnt_create_red(id, k_symstt(name), decl, pinfo);

	return info->supers[0];
}

// 엔트리 등록
kType k_tmnt_entry(const char* name, kType parent, kint size, kint flags, kTypeFunc t_init, kTypeFunc t_fini, khCnstFunc f_cnst, khDispFunc f_disp)
{
	kTypeDecl decl;
	decl.size = size;
	decl.flags = flags;
	decl.type_init = t_init;
	decl.type_fini = t_fini;
	decl.func_cnst = f_cnst;
	decl.func_disp = f_disp;
	return k_tmnt_reg(name, &decl, parent);
}

// 캐스트
kBase* k_tmnt_cast_linear(kType type, kBase* ptr)
{
	if (ptr)
	{
		kTypeInfo* info = ptr->type_info;
		kint i;

		for (i = 0; i <= info->scnt; i++)
		{
			if (type == info->supers[i])
				return ptr;
		}
	}

	return NULL;
}

// 캐스트
kBase* k_tmnt_cast_assert(kType type, kBase* ptr)
{
	if (ptr)
	{
		kTypeInfo* info = ptr->type_info;
		kTypeInfo* target;
		kint i;

		for (i = 0; i <= info->scnt; i++)
		{
			if (type == info->supers[i])
				return ptr;
		}

		// 캐스트 할 수 없음
		target = k_tmnt_look_type(type);

		if (k_candebug())
		{
			k_tracef(KLV_ERROR, "k.type", "cannot cast to '%s' type from '%s'", target->name, info->name);
			k_breakpoint();
		}
		else
		{
			// 할트
			k_tracef(KLV_FATAL, "k.type", "cannot cast to '%s' type from '%s'", target->name, info->name);
		}
	}

	return NULL;
}

// 타입 하나 출력
static void _k_tmnt_type_print(kType type)
{
	char flags[10], name[64], base[64];
	const char* pname;
	const char* pbase;
	ksize_t len;

	kTypeInfo* info = k_tmnt_look_type(type);

	len = k_strlen(info->name);

	if (len < 18)
		pname = info->name;
	else
	{
		pname = name;
		k_strncpy(name, info->name, 7);
		k_strcat(name, "...");
		k_strncpy(flags, info->name + (len - 7), 7);
		k_strcat(name, flags);
	}

	if (!info->parent)
		pbase = "--";
	else
	{
		len = k_strlen(info->parent->name);

		if (len < 18)
			pbase = info->parent->name;
		else
		{
			pbase = base;
			k_strncpy(base, info->parent->name, 7);
			k_strcat(base, "...");
			k_strncpy(flags, info->parent->name + (len - 7), 7);
			k_strcat(base, flags);
		}
	}

	flags[0] = (info->flags & KTIF_SEALED) ? 'S' : '_';
	flags[1] = (info->flags & KTIF_ABSTRACT) ? 'A' : '_';
	flags[2] = (info->flags & KTIF_VALUE) ? 'V' : '_';
	flags[3] = (info->flags & KTIF_REF) ? 'R' : '_';
	flags[4] = '\0';

	k_mesgf("%c%-17s | %4d | %4Lu | %04X | %s | %4d/%4d | %-15s\n",
		info->flags & KTIF_RED ? '@' : ' ', pname,
		info->size, info->refs, info->roam, flags,
		info->ccnt, info->scnt, pbase);
}

static void _k_tmnt_type_print_tree(kType type, kint* depth)
{
	kTypeInfo* info = k_tmnt_look_type(type);
	char sz[1024];
	kint i;

	sz[k_strfll(sz, 0, *depth, ' ')] = '\0';
	k_mesgf("%c[%d] %s%s (%Lu/%Lu)\n",
		info->flags & KTIF_RED ? '@' : ' ', *depth,
		sz, info->name, info->refs, info->accs);

	for (i = 0; i < info->ccnt; i++)
	{
		(*depth)++;
		_k_tmnt_type_print_tree(info->children[i], depth);
		(*depth)--;
	}
}

// 출력
kvoid k_tmnt_debug_print(kcham bytree)
{
	kTiNode* node;

	if (bytree)
	{
		kint depth = 0;

		TYPE_LOCK;

		for (node = k_impl_type.hash.frst; node; node = node->next)
		{
			kTypeInfo* info = k_tmnt_look_type(node->value);

			if (info->scnt != 0)
				continue;

			_k_tmnt_type_print_tree(node->value, &depth);
		}

		TYPE_UNLOCK;
	}
	else
	{
		k_mesgf(" %-17s | %-4s | %-4s | %-4s | %-4s | %-4s/%-4s | %-15s \n",
			"name", "size", "ref", "roam", "flag", "ccnt", "sdep", "parent");

		TYPE_LOCK;

		for (node = k_impl_type.hash.frst; node; node = node->next)
			_k_tmnt_type_print(node->value);

		TYPE_UNLOCK;
	}

	k_mesgf("total: %d\n", k_impl_type.hash.count);
}

//////////////////////////////////////////////////////////////////////////

// 타입 정보
kTypeInfo* k_type_get_info(kType type)
{
	kTypeInfo* info = k_tmnt_look_type(type);

	return info;
}

// 이름
const char* k_type_get_name(kType type)
{
	kTypeInfo* info = k_tmnt_look_type(type);

	return info->name;
}

// 설명
const char* k_type_get_desc(kType type)
{
	kTypeInfo* info = k_tmnt_look_type(type);

	return info->desc;
}

// 심볼
kuint k_type_get_sym(kType type)
{
	kTypeInfo* info = k_tmnt_look_type(type);

	return info->sym;
}

// 아이디
kint k_type_get_id(kType type)
{
	kTypeInfo* info = k_tmnt_look_type(type);

	return info->id;
}

// 값 타입
kcham k_type_is_value(kType type)
{
	kTypeInfo* info = k_tmnt_look_type(type);

	return info->isval;
}

// 베이스 타입
kcham k_type_is_ref(kType type)
{
	kTypeInfo* info = k_tmnt_look_type(type);

	return info->isref;
}

// 크기
kint k_type_get_size(kType type)
{
	kTypeInfo* info = k_tmnt_look_type(type);

	return info->size;
}

// 롬
kint k_type_get_roam(kType type)
{
	kTypeInfo* info = k_tmnt_look_type(type);

	return info->roam;
}

// 플래그
kint k_type_get_flags(kType type, kint mask)
{
	kTypeInfo* info = k_tmnt_look_type(type);

	return K_OMASK(info->flags, mask);
}

// 수퍼 개수
kint k_type_get_supers(kType type)
{
	kTypeInfo* info = k_tmnt_look_type(type);

	return info->scnt;
}

// 자식 개수
kint k_type_get_children(kType type)
{
	kTypeInfo* info = k_tmnt_look_type(type);

	return info->ccnt;
}

// 자식
kType k_type_get_child_nth(kType type, kint at)
{
	kTypeInfo* info = k_tmnt_look_type(type);

	return info->children[at];
}

// 부모
kType k_type_get_parent(kType type)
{
	kTypeInfo* info = k_tmnt_look_type(type);

	return (kType)info->parent;
}

// 연산자
khOpFunc k_type_get_op(kType type, kint op)
{
	kTypeInfo* info = k_tmnt_look_type(type);
	k_assert_0(op >= 0);

	return info->optbl.ops[op];
}

// 연산자, 파라미터로 찾으면 참임
kcham k_type_find_op(kType type, kuint op, khParamDesc** param, khOpFunc* opf)
{
	kint mask = kh_op_to_mask(op);
	kTypeInfo* info;

	info = k_tmnt_look_type(type);
	*opf = info->optbl.ops[op];

	for (; info; info = info->parent)
	{
		if (info->opov & mask)
		{
			*param = kh_prmshed_try(k_impl_type.prms, info->supers[0], kh_op_to_sym(op), FALSE);
			return TRUE;
		}
	}

	*param = NULL;
	return FALSE;
}

// 파라미터 얻기
khParamDesc* k_type_get_param(kType type, const char* name)
{
	return kh_prmshed_get(k_impl_type.prms, type, name, TRUE);
}

// 파라미터 얻기
khParamDesc* k_type_find_param(kType type, kuint symbol)
{
	return kh_prmshed_try(k_impl_type.prms, type, symbol, TRUE);
}

// 설명
void k_type_set_desc(kType type, const char* desc)
{
	kTypeInfo* info = k_tmnt_look_type(type);

	if (info->desc)
		k_delete(info->desc);

	info->desc = desc ? k_strdup(desc) : NULL;
}

// 롬
void k_type_set_roam(kType type, kuint roam)
{
	kTypeInfo* info = k_tmnt_look_type(type);

	info->roam = roam;
}

// 키
void k_type_set_func_key(kType type, khKeyFunc func)
{
	kTypeInfo* info = k_tmnt_look_type(type);

	info->func_key = func;
}

// 해시
void k_type_set_func_hash(kType type, khHashFunc func)
{
	kTypeInfo* info = k_tmnt_look_type(type);

	info->func_hash = func;
}

// 비교
void k_type_set_func_eq(kType type, khEqFunc func)
{
	kTypeInfo* info = k_tmnt_look_type(type);

	info->func_eq = func;
}
//
void k_type_set_func_type_fini(kType type, kTypeFunc func)
{
	kTypeInfo* info = k_tmnt_look_type(type);

	info->type_fini = func;
}

//
void k_type_set_func_base_cnst(kType type, khCnstFunc func)
{
	kTypeInfo* info = k_tmnt_look_type(type);

	info->func_cnst = func;
}

//
void k_type_set_func_base_disp(kType type, khDispFunc func)
{
	kTypeInfo* info = k_tmnt_look_type(type);

	info->func_disp = func;
}

// 연산자 설정
kcham k_type_set_ops(kType type, kint op, khOpFunc opf)
{
	kTypeInfo* info;

	k_return_value_if_fail(op >= 0 && op < KHOP_MAX_VALUE, FALSE);

	info = k_tmnt_look_type(type);
	k_return_value_if_fail(info != NULL, FALSE);

	info->optbl.ops[op] = opf;

	return TRUE;
}

// 연산자 테이블
kcham k_type_set_optable(kType type, const struct khOpTable* opt)
{
	kTypeInfo* info;
	const union khOpBind* src;
	union khOpBind* dst;
	int i;

	k_return_value_if_fail(opt != NULL, FALSE);

	info = k_tmnt_look_type(type);
	k_return_value_if_fail(info != NULL, FALSE);

	src = (const union khOpBind*)opt;
	dst = (union khOpBind*)&info->optbl;

	for (i = 0; i < KHOP_MAX_VALUE; i++)
	{
		if (!src->ops[i])
			continue;

		dst->ops[i] = src->ops[i];
	}

	return TRUE;
}

// 파라미터 추가
kcham k_type_bind_param(kType type, kint id, khParamDesc* param)
{
	k_return_value_if_fail(id > 0, FALSE);

	if (kh_prmshed_try(k_impl_type.prms, type, param->key, FALSE))
	{
		k_tracef(KLV_WARNING, "k.type", "bind failed: type '%s' already has a parameter '%s'",
			k_type_get_name(type), param->name);
		return FALSE;
	}

	return kh_prmshed_set(k_impl_type.prms, type, param);
}

// 프로퍼티(파라미터) 추가
kint k_type_bind_props(kType type, kint baseid, kint count, const struct khParamPropReg* regs)
{
	int i, cnt;
	khParamDesc* param;

	k_return_value_if_fail(baseid > 0, 0);
	k_return_value_if_fail(regs != NULL, 0);

	for (cnt = i = 0; i < count; i++)
	{
		if (!regs[i].name)
			break;

		param = kh_prmshed_get(k_impl_type.prms, type, regs[i].name, FALSE);

		if (param)
		{
			k_tracef(KLV_WARNING, "k.type", "bind failed: type '%s' already has a parameter '%s'",
				k_type_get_name(type), param->name);
			continue;
		}

		param = kh_param_alloc_prop(regs[i].name, regs[i].desc, regs[i].prop, regs[i].offset, regs[i].flags);

		if (!param)
		{
			k_tracef(KLV_WARNING, "k.type", "bind failed: invalid property '%s' for type '%s'",
				param->name, k_type_get_name(type));
			continue;
		}

		if (kh_prmshed_set(k_impl_type.prms, type, param))
			cnt++;
	}

	return cnt;
}

// 함수(파라미터) 추가
kint k_type_bind_funcs(kType type, kint baseid, kint count, const struct khParamFuncReg* regs)
{
	int i, cnt;
	khParamDesc* param;

	k_return_value_if_fail(baseid > 0, 0);
	k_return_value_if_fail(regs != NULL, 0);

	for (cnt = i = 0; i < count; i++)
	{
		if (!regs[i].name)
			break;

		param = kh_prmshed_get(k_impl_type.prms, type, regs[i].name, FALSE);

		if (param)
		{
			k_tracef(KLV_WARNING, "k.type", "bind failed: type '%s' already has a parameter '%s'",
				k_type_get_name(type), param->name);
			continue;
		}

		param = kh_param_alloc_func(regs[i].name, regs[i].desc, regs[i].func, regs[i].data, regs[i].flags);

		if (!param)
		{
			k_tracef(KLV_WARNING, "k.type", "bind failed: invalid function '%s' for type '%s'",
				param->name, k_type_get_name(type));
			continue;
		}

		if (kh_prmshed_set(k_impl_type.prms, type, param))
			cnt++;
	}

	return cnt;
}


//////////////////////////////////////////////////////////////////////////
// 파라미터 

// 파람 해시
typedef struct khPmNode
{
	struct khPmNode* sib;
	ksize_t hash;

	khParamDesc* key;
	khParamDesc* value;
} khPmNode;

typedef struct khPmHash
{
	ksize_t bucket;
	ksize_t count;
	khPmNode** nodes;
} khPmHash;

//
static khPmNode** _kh_pmhash_lookup(khPmHash* self, khParamDesc* key, ksize_t* rethash)
{
	khPmNode** ln;
	khPmNode* lnn;
	ksize_t lh;

	lh = (ksize_t)key->type;
#ifdef _K_64_
	lh = (lh << 32) - lh + key->key;
#else
	lh = (lh << 16) - lh + key->key;
#endif

	ln = &self->nodes[lh % self->bucket];

	while ((lnn = *ln) != NULL)
	{
		if (lnn->hash == lh && lnn->key->type == key->type && lnn->key->key == key->key)
			break;

		ln = &lnn->sib;
	}

	if (rethash)
		*rethash = lh;

	return ln;
}

//
static khPmNode* _kh_pmhash_get(khPmHash* self, khParamDesc* key)
{
	khPmNode** gnn;

	gnn = _kh_pmhash_lookup(self, key, NULL);

	return *gnn;
}

//
static void _kh_pmhash_test(khPmHash* self)
{
	ksize_t cnt = self->count;
	ksize_t bkt = self->bucket;

	if ((bkt >= 3 * cnt && bkt > K_MIN_HASH) || (3 * bkt <= cnt && bkt < K_MAX_HASH))
	{
		ksize_t i, newbucket;
		khPmNode** newnodes;

		newbucket = k_primeclose(self->count);
		newbucket = K_CLAMP(newbucket, K_MIN_HASH, K_MAX_HASH);
		newnodes = k_new_zero(newbucket, khPmNode*);

		for (i = 0; i < self->bucket; i++)
		{
			khPmNode* node;
			khPmNode* next;
			ksize_t mask;

			for (node = self->nodes[i]; node; node = next)
			{
				next = node->sib;
				mask = node->hash % newbucket;
				node->sib = newnodes[mask];
				newnodes[mask] = node;
			}
		}

		k_delete(self->nodes);
		self->nodes = newnodes;
		self->bucket = newbucket;
	}
}

// 파라미터 창고 구현
struct khParamShed
{
	kMutex* lock;
	khPmHash hash;
};

//
static khParamPropFunc _kh_prm_prop_getter(kuint prop);
static khParamPropFunc _kh_prm_prop_setter(kuint prop);

// 만들기
khParamShed* kh_prmshed_new(void)
{
	khParamShed* self = k_new_1(khParamShed);

	self->lock = k_mutex_new();

	self->hash.count = 0;
	self->hash.bucket = K_MIN_HASH;
	self->hash.nodes = k_new_zero(K_MIN_HASH, khPmNode*);

	return self;
}

// 제거
void kh_prmshed_delete(khParamShed* self)
{
	khPmNode* node;
	khPmNode* next;
	ksize_t i;

	k_mutex_enter(self->lock);

	for (i = 0; i < self->hash.bucket; i++)
	{
		for (node = self->hash.nodes[i]; node; node = next)
		{
			next = node->sib;
			kh_param_free(node->value);
			k_free(node);
		}
	}

	k_free(self->hash.nodes);

	k_mutex_leave(self->lock);
	k_mutex_delete(self->lock);

	k_free(self);
}

// 넣기
kcham kh_prmshed_set(khParamShed* self, kType type, khParamDesc* desc)
{
	khPmNode** an;
	khPmNode* ann;
	ksize_t ah;

	k_return_value_if_fail(type > 0, FALSE);
	k_return_value_if_fail(desc != NULL, FALSE);
	k_return_value_if_fail(desc->type == 0, FALSE);
	k_return_value_if_fail(desc->key != 0, FALSE);

	desc->type = type;

	k_mutex_enter(self->lock);

	an = _kh_pmhash_lookup(&self->hash, desc, &ah);

	ann = *an;

	if (ann)
	{
		// value 제거
		ann->key = desc;
		ann->value = desc;
	}
	else
	{
		ann = k_new_1(khPmNode);
		ann->sib = NULL;
		ann->hash = ah;
		ann->key = desc;
		ann->value = desc;

		*an = ann;
		self->hash.count++;

		_kh_pmhash_test(&self->hash);
	}

	k_mutex_leave(self->lock);

	return TRUE;
}

// 제거
kcham kh_prmshed_remove(khParamShed* self, khParamDesc* desc)
{
	khPmNode** rn;
	khPmNode* rnn;
	kcham ret;

	k_return_value_if_fail(desc != NULL, FALSE);

	k_mutex_enter(self->lock);

	rn = _kh_pmhash_lookup(&self->hash, desc, NULL);
	rnn = *rn;

	if (!rnn)
		ret = FALSE;
	else
	{
		*rn = rnn->sib;

		kh_param_free(rnn->value);
		k_delete(rnn);

		self->hash.count--;
		_kh_pmhash_test(&self->hash);

		ret = TRUE;
	}

	k_mutex_leave(self->lock);

	return ret;
}

// 루껍
khParamDesc* kh_prmshed_find(khParamShed* self, kType type, kuint sym, kcham recursive)
{
	khParamDesc key;
	khPmNode* node;

	key.type = type;
	key.key = sym;

	if (recursive)
	{
		k_mutex_enter(self->lock);

		do
		{
			node = _kh_pmhash_get(&self->hash, &key);

			if (node)
			{
				k_mutex_leave(self->lock);
				return node->value;
			}

			key.type = k_type_get_parent(key.type);
		} while (key.type);

		k_mutex_leave(self->lock);

		return NULL;
	}
	else
	{
		k_mutex_enter(self->lock);
		node = _kh_pmhash_get(&self->hash, &key);
		k_mutex_leave(self->lock);

		return node ? node->value : NULL;
	}
}

// 얻기
khParamDesc* kh_prmshed_get(khParamShed* self, kType type, const char* name, kcham recursive)
{
	kuint sym;

	k_return_value_if_fail(name != NULL, FALSE);

	sym = k_symtry(name);
	k_return_value_if_fail(sym != 0, FALSE);

	return kh_prmshed_find(self, type, sym, recursive);
}

// 편안하기
khParamDesc* kh_prmshed_try(khParamShed* self, kType type, kuint sym, kcham recursive)
{
	k_return_value_if_fail(sym != 0, FALSE);

	return kh_prmshed_find(self, type, sym, recursive);
}

//////////////////////////////////////////////////////////////////////////
// 파라미터

// 제거
void kh_param_free(khParamDesc* self)
{
	if ((self->flags & KHPRMF_DESC) == 0)
		k_delete(self->desc);

	if (self->prm == KHPRM_PROP)
	{
	}
	else if (self->prm == KHPRM_FUNC)
	{
		if (self->flags & KHPRMF_FREE_DATA)
			k_delete(((struct khParamFuncDesc*)self)->data);
	}

	k_delete(self);
}

// 만들기
khParamDesc* kh_param_alloc(ksize_t size, kint paramtype, const char* name, const char* desc, int flags)
{
	khParamDesc* self;

	k_return_value_if_fail(size >= sizeof(khParamDesc), NULL);
	k_return_value_if_fail(paramtype < 4, NULL);
	k_return_value_if_fail(name != NULL, NULL);

	self = (khParamDesc*)k_new(size, kbyte);
	self->prm = (enum khParamTypes)paramtype;
	self->flags = flags;
	self->type = 0;

	if (flags & KHPRMF_NAME)
		self->name = (char*)k_symbothstt(name, &self->key);
	else
		self->name = (char*)k_symbothflx(name, &self->key);

	if (flags & KHPRMF_DESC)
		self->desc = (char*)desc;
	else
		self->desc = desc ? k_strdup(desc) : NULL;

	return self;
}

// 만들기, 프로퍼티
khParamDesc* kh_param_alloc_prop(const char* name, const char* desc, kint prop, kint offset, kint flags)
{
	struct khParamPropDesc* self;

	k_return_value_if_fail(offset < K_MAX_USHORT, NULL);
	k_return_value_if_fail((flags & KHPRMF_RW) != 0, NULL);

	self = (struct khParamPropDesc*)kh_param_alloc(sizeof(struct khParamPropDesc), KHPRM_PROP, name, desc, flags);
	k_return_value_if_fail(self != NULL, NULL);

	self->prop = prop;
	self->offset = offset;
	self->getter = _kh_prm_prop_getter(prop);
	self->setter = _kh_prm_prop_setter(prop);

	return (khParamDesc*)self;
}

// 만들기, 함수
khParamDesc* kh_param_alloc_func(const char* name, const char* desc, khParamCallFunc func, kpointer data, kint flags)
{
	struct khParamFuncDesc* self;

	k_return_value_if_fail(func != NULL, NULL);

	self = (struct khParamFuncDesc*)kh_param_alloc(sizeof(struct khParamFuncDesc), KHPRM_FUNC, name, desc, flags);
	k_return_value_if_fail(self != NULL, NULL);

	self->func = func;
	self->data = data;

	return (khParamDesc*)self;
}


//////////////////////////////////////////////////////////////////////////
// 내부 프로퍼티

// cham
static void _kh_prm_prop_get_bool(kpointer ptr, ksize_t offset, kVar* var)
{
	kbool* p = (kbool*)(((kbyte*)ptr) + offset);
	k_var_set_bool(var, *p);
}

// cham
static void _kh_prm_prop_set_bool(kpointer ptr, ksize_t offset, kVar* var)
{
	kbool* p = (kbool*)(((kbyte*)ptr) + offset);
	k_var_conv_bool(var, p);
}

// float
static void _kh_prm_prop_get_float(kpointer ptr, ksize_t offset, kVar* var)
{
	float* p = (float*)(((kbyte*)ptr) + offset);
	k_var_set_float(var, *p);
}

// float
static void _kh_prm_prop_set_float(kpointer ptr, ksize_t offset, kVar* var)
{
	float* p = (float*)(((kbyte*)ptr) + offset);
	k_var_conv_float(var, p);
}

// pointer
static void _kh_prm_prop_get_ptr(kpointer ptr, ksize_t offset, kVar* var)
{
	kpointer* p = (kpointer*)(((kbyte*)ptr) + offset);
	k_var_set_ptr(var, *p);
}

// pointer
static void _kh_prm_prop_set_ptr(kpointer ptr, ksize_t offset, kVar* var)
{
	kpointer* p = (kpointer*)(((kbyte*)ptr) + offset);
	k_var_safe_ptr(var, p);
}

// base
static void _kh_prm_prop_get_base(kpointer ptr, ksize_t offset, kVar* var)
{
	kBase** p = (kBase**)(((kbyte*)ptr) + offset);
	k_var_set_base(var, *p);
}

// base
static void _kh_prm_prop_set_base(kpointer ptr, ksize_t offset, kVar* var)
{
	kBase** p = (kBase**)(((kbyte*)ptr) + offset);
	*p = k_var_unsafe_base(var);
}

// string
static void _kh_prm_prop_get_string(kpointer ptr, ksize_t offset, kVar* var)
{
	const char** p = (const char**)(((kbyte*)ptr) + offset);
	k_var_set_str(var, *p, -1);
}

// string
static void _kh_prm_prop_set_string(kpointer ptr, ksize_t offset, kVar* var)
{
	char** p = (char**)(((kbyte*)ptr) + offset);
	const char* psz = k_var_unsafe_str(var, NULL);
	k_strcpy(*p, psz);
	// redstr일 수 있으므로 언로드
	k_var_unload(var);
}

// sbyte
static void _kh_prm_prop_get_sbyte(kpointer ptr, ksize_t offset, kVar* var)
{
	kchar* p = (kchar*)(((kbyte*)ptr) + offset);
	k_var_set_int(var, *p);
}

// sbyte
static void _kh_prm_prop_set_sbyte(kpointer ptr, ksize_t offset, kVar* var)
{
	kchar* p = (kchar*)(((kbyte*)ptr) + offset);
	int t;
	k_var_conv_int(var, &t);
	*p = (kchar)t;
}

// short
static void _kh_prm_prop_get_short(kpointer ptr, ksize_t offset, kVar* var)
{
	kshort* p = (kshort*)(((kbyte*)ptr) + offset);
	k_var_set_int(var, *p);
}

// short
static void _kh_prm_prop_set_short(kpointer ptr, ksize_t offset, kVar* var)
{
	kshort* p = (kshort*)(((kbyte*)ptr) + offset);
	int t;
	k_var_conv_int(var, &t);
	*p = (kshort)t;
}

// int
static void _kh_prm_prop_get_int(kpointer ptr, ksize_t offset, kVar* var)
{
	kint* p = (kint*)(((kbyte*)ptr) + offset);
	k_var_set_int(var, *p);
}

// int
static void _kh_prm_prop_set_int(kpointer ptr, ksize_t offset, kVar* var)
{
	kint* p = (kint*)(((kbyte*)ptr) + offset);
	k_var_conv_int(var, p);
}

// long
static void _kh_prm_prop_get_long(kpointer ptr, ksize_t offset, kVar* var)
{
	klong* p = (klong*)(((kbyte*)ptr) + offset);
	k_var_set_long(var, *p);
}

// long
static void _kh_prm_prop_set_long(kpointer ptr, ksize_t offset, kVar* var)
{
	klong* p = (klong*)(((kbyte*)ptr) + offset);
	k_var_conv_long(var, p);
}

// byte
static void _kh_prm_prop_get_byte(kpointer ptr, ksize_t offset, kVar* var)
{
	kbyte* p = (kbyte*)(((kbyte*)ptr) + offset);
	k_var_set_int(var, *p);
}

// byte
static void _kh_prm_prop_set_byte(kpointer ptr, ksize_t offset, kVar* var)
{
	kbyte* p = (kbyte*)(((kbyte*)ptr) + offset);
	int t;
	k_var_conv_int(var, &t);
	*p = (kbyte)t;
}

// ushort
static void _kh_prm_prop_get_ushort(kpointer ptr, ksize_t offset, kVar* var)
{
	kushort* p = (kushort*)(((kbyte*)ptr) + offset);
	k_var_set_int(var, *p);
}

// ushort
static void _kh_prm_prop_set_ushort(kpointer ptr, ksize_t offset, kVar* var)
{
	kushort* p = (kushort*)(((kbyte*)ptr) + offset);
	int t;
	k_var_conv_int(var, &t);
	*p = (kushort)t;
}

// uint
static void _kh_prm_prop_get_uint(kpointer ptr, ksize_t offset, kVar* var)
{
	kuint* p = (kuint*)(((kbyte*)ptr) + offset);
	k_var_set_int(var, (int)*p);
}

// uint
static void _kh_prm_prop_set_uint(kpointer ptr, ksize_t offset, kVar* var)
{
	kuint* p = (kuint*)(((kbyte*)ptr) + offset);
	k_var_conv_int(var, (int*)p);
}

// ulong
static void _kh_prm_prop_get_ulong(kpointer ptr, ksize_t offset, kVar* var)
{
	kulong* p = (kulong*)(((kbyte*)ptr) + offset);
	k_var_set_long(var, (klong)*p);
}

// ulong
static void _kh_prm_prop_set_ulong(kpointer ptr, ksize_t offset, kVar* var)
{
	kulong* p = (kulong*)(((kbyte*)ptr) + offset);
	k_var_conv_long(var, (klong*)p);
}

// double
static void _kh_prm_prop_get_dbl(kpointer ptr, ksize_t offset, kVar* var)
{
	double* p = (double*)(((kbyte*)ptr) + offset);
	k_var_set_double(var, *p);
}

// double
static void _kh_prm_prop_set_dbl(kpointer ptr, ksize_t offset, kVar* var)
{
	double* p = (double*)(((kbyte*)ptr) + offset);
	k_var_conv_double(var, p);
}

// 얻기
static khParamPropFunc _kh_prm_prop_getter(kuint prop)
{
	static const khParamPropFunc s_getter[] =
	{
		_kh_prm_prop_get_bool,
		_kh_prm_prop_get_ptr,
		_kh_prm_prop_get_sbyte,
		_kh_prm_prop_get_short,
		_kh_prm_prop_get_int,
		_kh_prm_prop_get_long,
		_kh_prm_prop_get_byte,
		_kh_prm_prop_get_ushort,
		_kh_prm_prop_get_uint,
		_kh_prm_prop_get_ulong,
		_kh_prm_prop_get_float,
		_kh_prm_prop_get_dbl,
		_kh_prm_prop_get_base,
		_kh_prm_prop_get_string,
		_kh_prm_prop_get_string,
	};
	return prop < K_COUNTOF(s_getter) ? s_getter[prop] : NULL;
}

// 넣기
static khParamPropFunc _kh_prm_prop_setter(kuint prop)
{
	static const khParamPropFunc s_setter[] =
	{
		_kh_prm_prop_set_bool,
		_kh_prm_prop_set_ptr,
		_kh_prm_prop_set_sbyte,
		_kh_prm_prop_set_short,
		_kh_prm_prop_set_int,
		_kh_prm_prop_set_long,
		_kh_prm_prop_set_byte,
		_kh_prm_prop_set_ushort,
		_kh_prm_prop_set_uint,
		_kh_prm_prop_set_ulong,
		_kh_prm_prop_set_float,
		_kh_prm_prop_set_dbl,
		_kh_prm_prop_set_base,
		_kh_prm_prop_set_string,
		_kh_prm_prop_set_string,
	};
	return prop < K_COUNTOF(s_setter) ? s_setter[prop] : NULL;
}
